﻿#include "../../ctlScroll.h"
#include "../../ctlHandle.h"

namespace swui
{
	bool ctlScroll::create(ctlRoot* parent, ctlPoint pos, unsigned length,  
			ctlScrollDirection dir, const ctlStyleBase& style, const ctlStyleBase& btnsst)
	{
		if(parent == 0 || isCreated() || !parent->isCreated()) return false; 
		std::lock_guard<decltype(_eventTrans)> gurad(
			_eventTrans.getControlEventTrans(parent));
		const unsigned buttonSize = 15;
		_control_virtual_class_end = this;
		//计算控件的位置信息
		ctlRect rect(pos.x, pos.y, buttonSize, length);
		if(dir == ctlScrollDirection::horizontal)
			std::swap(rect.cx, rect.cy);
		//设置基本属性
		_basic_create_(rect, parent);
		_controlType = ctlControlType::ctlScroll;
		_scroll_direction = dir;
		_scroll_scrolled_logic_length = 0;
		_scroll_logic_length = 100;
		_scroll_logic_step = 30;
		lockDrawing();
		//计算控件渐变样式（如果有）的渐变方向
		auto gradientDirBk =  ctlStyleGradient::reverse_vertical, 
			gradientDir = ctlStyleGradient::vertical;
		if(dir == ctlScrollDirection::vertical)
		{
			gradientDirBk = ctlStyleGradient::reverse_horizontal;
			gradientDir = ctlStyleGradient::horizontal;
		}
		std::shared_ptr<ctlStyleBase> sst = decltype(sst)(btnsst.clone());
		if(btnsst.styleType == ctlStyleType::gradient)
			getStyleGradient(sst.get())->gradientDirection = gradientDir;
		//创建按钮控件
		_scroll_top.create(this, ctlRect(0, 0, buttonSize, buttonSize), "", *sst);
		if(dir == ctlScrollDirection::horizontal) 
		{
			_scroll_bottom.create(this, ctlRect(getWidth() - buttonSize, 0, buttonSize, buttonSize), "", *sst);
			_scroll_bar.create(this, ctlRect(buttonSize, 0, 1, buttonSize), "", *sst);
			_scroll_bar.setSizeFormat(ctlRoot::sizeFormat::percent, ctlDirection::y);
			//添加按钮箭头
			_scroll_top.addLambdaEvent(ctlNotifyEventType::draw, 
				createTriangleButtonHandle(ctlTriangleDirection::left, 0.4f, 0.4f, 0x77000000));
			_scroll_bottom.addLambdaEvent(ctlNotifyEventType::draw, 
				createTriangleButtonHandle(ctlTriangleDirection::right, 0.4f, 0.4f, 0x77000000));
		} else {
			_scroll_bottom.create(this, ctlRect(0, getHeight() - buttonSize, buttonSize, buttonSize), "", *sst);
			_scroll_bar.create(this, ctlRect(0, buttonSize, buttonSize, 1), "", *sst);
			_scroll_bar.setSizeFormat(ctlRoot::sizeFormat::percent, ctlDirection::x);
			//添加按钮箭头
			_scroll_top.addLambdaEvent(ctlNotifyEventType::draw, 
				createTriangleButtonHandle(ctlTriangleDirection::top, 0.4f, 0.4f, 0x77000000));
			_scroll_bottom.addLambdaEvent(ctlNotifyEventType::draw, 
				createTriangleButtonHandle(ctlTriangleDirection::bottom, 0.4f, 0.4f, 0x77000000));
		}
		//启用拖放
		_scroll_bar.enableDrag();
		_scroll_bar.addLambdaEvent(ctlNotifyEventType::drag, 
			[this](ctlRoot* root, ctlNotifyEvent* ne)
			{
				ctlNotifyDrag *drag = (ctlNotifyDrag*)ne->info;
				if(drag->state == ctlNotifyDrag::move)
				{
					int pos = drag->position.y;
					if(getScrollDirection() == ctlScrollDirection::horizontal)
						pos = drag->position.x;
					pos -= getRealScrollSize() - 1;
					setScrolledLogicLength(int(float(pos) / getRealScrollableLength() * getMaxLogicLength()));
				}
			});
		//设置按钮按下的事件
		_scroll_top.addLambdaEvent(ctlMouseEventType::leftDown, 
			[this](ctlRoot* root, ctlMouseEvent* me)
			{
				int delta = (int)ctlMax(getLogicStep() / 2, 1);
				addScrolledLogicLength(-delta);
				_eventTrans.setTimer(50, [this]() -> bool
				{
					if(getScrolledLogicLength() == 0 || !getTopButton()->isMouseDown())
						return false;
					int delta = (int)ctlMax(getLogicStep() / 2, 1);
					addScrolledLogicLength(-delta);
					return true;
				} );
			} );
		_scroll_bottom.addLambdaEvent(ctlMouseEventType::leftDown, 
			[this](ctlRoot* root, ctlMouseEvent* me)
			{
				int delta = (int)ctlMax(getLogicStep() / 2, 1);
				addScrolledLogicLength(delta);
				_eventTrans.setTimer(50, [this]() -> bool
				{
					if(getScrolledLogicLength() == getMaxLogicLength()
						|| !getBottomButton()->isMouseDown())
						return false;
					int delta = (int)ctlMax(getLogicStep() / 2, 1);
					addScrolledLogicLength(delta);
					return true;
				} );
			} );
		//设置焦点转让
		_scroll_bottom.setFocusAssign(this);
		_scroll_bar.setFocusAssign(this);
		_scroll_top.setFocusAssign(this);
		//设置相对定位
		_scroll_bottom.setMoveFormat(ctlRoot::moveFormat::relative, ctlDirection::xy, this);
		_scroll_bottom.setMoveAlignment(ctlRoot::moveAlignment::rightBottom);
		_scroll_bottom.moveRelative(0, 0);
		//修正值
		_flush_scroll_bar_length();
		if(style.styleType == ctlStyleType::gradient)
		{
			ctlStyleBase* clonestyle = style.clone();
			getStyleGradient(clonestyle)->gradientDirection = gradientDirBk;
			setStyle(*clonestyle);
			delete clonestyle;
		} else setStyle(style);
		unlockDrawing();
		redraw();
		return true;
	}
	bool ctlScroll::setMaxLogicLength(int length, bool keepPos)
	{
		if(keepPos)
		{
			_scroll_logic_length = length;
			//如果允许的最大逻辑长度比已经滚动的逻辑长度大
			if(length < getScrolledLogicLength()) 
				setScrolledLogicLength(length);
		} else {
			//计算需要的逻辑长度
			unsigned val = unsigned(getScrolledPercent() * length);
			_scroll_logic_length = length;
			setScrolledLogicLength(val);
		}
		_flush_scroll_bar_length();
		return true;
	}
	bool ctlScroll::setScrolledLogicLength(int logicLength)
	{
		if(!isCreated() || !isEnabled()) return false; 
		if(logicLength < 0) logicLength = 0;
		if(logicLength > getMaxLogicLength()) 
			logicLength = getMaxLogicLength();
		if(logicLength == getScrolledLogicLength())
			return true;
		ctlNotifyEvent ne;
		ne.eventType = ctlNotifyEventType::scroll;
		ctlNotifyScroll ns;
		ne.info = &ns;
		bool allow = true;
		ns.isAllow = &allow;
		ns.logicLength = logicLength;
		ns.percent = float(logicLength) / getMaxLogicLength();
		_notify_event_(&ne);
		if(allow) 
		{
			_scroll_scrolled_logic_length = logicLength;
			int len = int(getScrolledPercent() * 
				getRealScrollableLength() + getRealScrollSize()) - 1;	//边框有 1px
			if(getScrollDirection() == ctlScrollDirection::horizontal)
				getBarButton()->move(len, 0);
			else getBarButton()->move(0, len);
			return true;
		}
		return false;
	}
	void ctlScroll::_flush_scroll_bar_length()
	{
		int length = int(float(getRealLengthWithoutButton()) / float(getMaxLogicLength()) * 100.0f);
		if(length > int(getRealLength() / 1.5f) - getRealScrollSize())
			length = int(getRealLength() / 1.5f) - getRealScrollSize();
		if(length < 8) length = 8;
		int scrollable_length = getRealLengthWithoutButton() - length;
		int moveRange = int(getScrolledPercent() * 
				scrollable_length + getRealScrollSize());
		if(getScrollDirection() == ctlScrollDirection::horizontal)
		{
			getBarButton()->setDragableRect(ctlRect(getRealScrollSize() - 1, 0, 
				getRealLengthWithoutButton(), getHeight()));
			getBarButton()->move(moveRange - 1, 0);
			getBarButton()->size(length, getRealScrollSize());
		} else {
			getBarButton()->setDragableRect(ctlRect(0, getRealScrollSize() - 1, 
				getWidth(), getRealLengthWithoutButton()));
			getBarButton()->move(0, moveRange - 1);
			getBarButton()->size(getRealScrollSize(), length);
		}
	}
	void ctlScroll::_position_event_(ctlPositionEvent* pe)
	{
		if(pe->eventType == ctlPositionEventType::size)
		{
			lockDrawing();
			int len = getRealScrollSize();
			getBottomButton()->size(len, len);
			getTopButton()->size(len, len);
			_flush_scroll_bar_length();
			unlockDrawing();
			redraw();
		}
		ctlRoot::_position_event_(pe);
	}
	void ctlScroll::_mouse_event_(ctlMouseEvent* me)
	{
		if(me->eventModel != ctlMouseEventModel::cut) return;
		switch(me->eventType)
		{
		case ctlMouseEventType::wheel:
			dealMouseWheelEvent(me);
			break;
		case ctlMouseEventType::leftDown:
			{
				ctlPoint mpos = me->mousePos;
				auto lambdaDeal = [this, mpos]() -> bool
				{
					if(isMouseDown() == false)
						return false;
					if(getScrollDirection() == ctlScrollDirection::vertical)
					{
						if(mpos.y < getBarButton()->getTop())
						{
							setScrolledLogicLength(getScrolledLogicLength()
								- getLogicStep() * 2);
						} else if(mpos.y > getBarButton()->getBottom()) {
							setScrolledLogicLength(getScrolledLogicLength()
								+ getLogicStep() * 2);
						} else return false;
					} else {
						if(mpos.x < getBarButton()->getLeft())
						{
							setScrolledLogicLength(getScrolledLogicLength()
								- getLogicStep() * 2);
						} else if(mpos.x > getBarButton()->getRight()) {
							setScrolledLogicLength(getScrolledLogicLength()
								+ getLogicStep() * 2);
						} else return false;
					}
					return true;
				};
				lambdaDeal();
				_eventTrans.setTimer(120, lambdaDeal);
			}
			break;
		}
		ctlRoot::_mouse_event_(me);
		ISmoothTransform::_smooth_transform_mouse_event(me);
	}
	void ctlScroll::_key_event_(ctlKeyEvent* ke)
	{
		if(ke->eventType == ctlKeyEventType::down)
		{
			int delta = 0;
			if(getScrollDirection() == ctlScrollDirection::horizontal)
			{
				if(ke->charInfo.virtualKey == VK_LEFT)
					delta = -getLogicStep();
				else if(ke->charInfo.virtualKey == VK_RIGHT)
					delta = getLogicStep();
			} else {
				if(ke->charInfo.virtualKey == VK_UP)
					delta = -getLogicStep();
				else if(ke->charInfo.virtualKey == VK_DOWN)
					delta = getLogicStep();
			}
			addScrolledLogicLength(delta);
		}
		ctlRoot::_key_event_(ke);
	}
}